//
//  MDHHTTPService.m
//  JSTech
//
//  Created by 高得华 on 2019/4/23.
//  Copyright © 2020 华&梅. All rights reserved.
//

#import "MDHHTTPService.h"

#if __has_include("MDHPT/MDHPT-Swift.h")
#import "MDHPT/MDHPT-Swift.h"
#else
#import "JSTech-Swift.h"
#endif

#if __has_include(<AFNetworking/AFNetworking-umbrella.h>)
#import <AFNetworking/AFNetworking-umbrella.h>
#else
#import "AFNetworking-umbrella.h"
#endif

#if __has_include(<MJExtension/MJExtension.h>)
#import <MJExtension/MJExtension.h>
#else
#import "MJExtension.h"
#endif

#if __has_include(<YYCategories/YYCategories.h>)
#import <YYCategories/YYCategories.h>
#else
#import "YYCategories.h"
#endif

/**
 * 知识点
 //- (RACSignal *)replayLast 就是用Capacity为1的RACReplaySubject来替换- (RACSignal *)replay的`subject。
 其作用是使后来订阅者只收到最后的历史值。
 //- (RACSignal *)replayLazily和- (RACSignal *)replay的区别就是replayLazily会在第一次订阅的时候才订阅sourceSignal。
 //  replay、replayLast、replayLazily的区别 ReactiveCocoa提供了这三个简便的方法允许多个订阅者订阅一个信号，却不会重复执行订阅代码，并且能给新加的订阅者提供订阅前的值。
 replay和replayLast使信号变成热信号，且会提供所有值(-replay) 或者最新的值(-replayLast) 给订阅者。
 replayLazily 会提供所有的值给订阅者 replayLazily还是冷信号 避免了冷信号的副作用
 *
 */

//MARK: - ===== 静态变量 ===== -
///服务端返回的完整数据，需要在错误的信息的时候解析即可
NSString * _Nullable const MDHHTTPServiceResponseErrorDataKey = @"data";
//=========== 静态变量
///基础的请求网址 baseUrl
static NSString * dh_privateNetworkBaseUrl = @"";
///请求错误时的 code 取值 MHHTTPServiceErrorResponseCodeKey
static NSString * dh_ErrorResponseCodeKey = @"";
///SignKey 签名key
static NSString * dh_SignKey = @"";
///请求错误时的 code 取值
static NSString * dh_PrivateKey = @"";
///请求错误时的 code 取值
static NSString * dh_RequestTokenKey = @"";

///请求错误时的 code 取值
static NSString * dh_ResponseDataListKey = @"";
///SignKey 签名key
static NSString * dh_ResponseDataKey = @"";
///请求错误时的 code 取值
static NSString * dh_ResponseMsgKey = @"";
///请求错误时的 code 取值
static NSString * dh_ResponseCodeKey = @"";


@interface MDHHTTPService ()

///当前登录用户信息
@property (nonatomic, readwrite, strong) MDHUserModel * currentUser;

@end

@implementation MDHHTTPService

@synthesize token = _token;
@synthesize version = _version;

///设置 基础的 请求地址
+ (void)setupBaseUrl:(NSString *)baseUrl {
    dh_privateNetworkBaseUrl = baseUrl;
}
///dh_ErrorResponseCodeKey 错误信息时 的code 对应key
+ (void)setupErrorResponseCodeKey:(NSString *)codeKey {
    dh_ErrorResponseCodeKey = codeKey;
}
///SignKey 签名key
+ (void)setupSignKey:(NSString *)signKey {
    dh_SignKey = signKey;
}
/// 私钥Value KeyValue
+ (void)setupPrivateKey:(NSString *)key {
    dh_PrivateKey = key;
}
///获取 私密的 对应的值
+ (NSString *)privateKey {
    return dh_PrivateKey;
}
///RequestTokenKey 服务器相关 服务器返回token 对应的key
+ (void)setupRequestTokenKey:(NSString *)key {
    dh_RequestTokenKey = key;
}
/// ResponseCodeKey /// 状态码key
+ (void)setupResponseCodeKey:(NSString *)key {
    dh_ResponseCodeKey = key;
}
/// 消息key
+ (void)setupResponseMsgKey:(NSString *)key {
    dh_ResponseMsgKey = key;
}
/// 数据data
+ (void)setupResponseDataKey:(NSString *)key {
    dh_ResponseDataKey = key;
}
/// 数据data{"list":[]}
+ (void)setupResponseDataListKey:(NSString *)key {
    dh_ResponseDataListKey = key;
}

static MDHHTTPService * service_ = nil;
+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (dh_privateNetworkBaseUrl.length <= 0) {
            AlertViewShowOne(@"基础的请求网络URL为空,请进行配置");
        }
        service_ = [[self alloc] initWithBaseURL:[NSURL URLWithString:dh_privateNetworkBaseUrl] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    });
    return service_;
}

+ (id)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        service_ = [super allocWithZone:zone];
    });
    return service_;
}
- (id)copyWithZone:(NSZone *)zone {
    return service_;
}
- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration{
    if (self = [super initWithBaseURL:url sessionConfiguration:configuration]) {
        /// 配置
        [self _configHTTPService];
    }
    return self;
}

///检测 是否代理
- (BOOL)getProxyStatus {
    ///
    BOOL is_agent = self.currentUser.is_agent;
    if (is_agent) {
        return false;
    }
    
    NSDictionary *proxySettings =  (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:dh_privateNetworkBaseUrl]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
    NSDictionary *settings = [proxies objectAtIndex:0];
    
    DTLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
    DTLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
    DTLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
    
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]){
        //没有设置代理
        return false;
    }else{
        //设置代理了
        return true;
    }
}

/// config service
- (void)_configHTTPService {
    ///响应配置
    AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer];
#if DEBUG
    responseSerializer.removesKeysWithNullValues = NO;
#else
    responseSerializer.removesKeysWithNullValues = YES;
#endif
    responseSerializer.readingOptions = NSJSONReadingAllowFragments;
    /// config
    ///错误信息 - 可以使用这个进行查看x
    self.responseSerializer = responseSerializer;//[AFHTTPResponseSerializer serializer];//
    
    ///请求设置配置
    AFHTTPRequestSerializer * requestSerializer = [AFHTTPRequestSerializer serializer];
    /*
     //使用协议缓存策略，也就是按照响应头的HTTP缓存字段来使用缓存
     NSURLRequestUseProtocolCachePolicy
     //忽略缓存
     NSURLRequestReloadIgnoringLocalCacheData
     //有缓存则读缓存（就算缓存过期也读取)，没有则网络加载
     NSURLRequestReturnCacheDataElseLoad
     //有缓存则读缓存（就算缓存过期也读取)，没有则报错
     NSURLRequestReturnCacheDataDontLoad
     */
    //requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
    ///改变网络超时
    [requestSerializer willChangeValueForKey:@"timeoutInterval"];
#if DEBUG
    ///网络超时配置
    requestSerializer.timeoutInterval = 30;
#else
    ///网络超时配置
    requestSerializer.timeoutInterval = 15;
#endif
    [requestSerializer didChangeValueForKey:@"timeoutInterval"];

    self.requestSerializer = requestSerializer;

    ///Https的配置信息
    MMConfigModel * model = [AppDelegate shared].dataHttps;
    NSData * cerData = [model.api_url_ssl_ios convertHexStrToData];
    
    /*
     //不使用固定证书（本地）验证服务器。直接从客户端系统中的受信任颁发机构 CA 列表中去验证
         AFSSLPinningModeNone,

         // 代表会对服务器返回的证书中的PublicKey进行验证，通过则通过，否则不通过
         AFSSLPinningModePublicKey,

         // 代表会对服务器返回的证书同本地证书全部进行校验，通过则通过，否则不通过
         AFSSLPinningModeCertificate,
     */
    /// 安全策略
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    if (cerData) {
        securityPolicy.pinnedCertificates = [[NSSet alloc] initWithObjects:cerData, nil];
    }
    //allowInvalidCertificates 是否允许无效证书（也就是自建的证书），默认为NO
    //如果是需要验证自建证书，需要设置为YES
    securityPolicy.allowInvalidCertificates = true;
    //validatesDomainName 是否需要验证域名，默认为YES；
    //假如证书的域名与你请求的域名不一致，需把该项设置为NO
    //主要用于这种情况：客户端请求的是子域名，而证书上的是另外一个域名。因为SSL证书上的域名是独立的，假如证书上注册的域名是www.google.com，那么mail.google.com是无法验证通过的；当然，有钱可以注册通配符的域名*.google.com，但这个还是比较贵的。
    securityPolicy.validatesDomainName = false;
    
    // 2.设置证书模式
    //securityPolicy.
    self.securityPolicy = securityPolicy;
    /// 支持解析
    self.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:
                                                      @"application/json",
                                                      @"text/json",
                                                      @"text/javascript",
                                                      @"text/html",
                                                      @"text/plain",
                                                      @"text/html; charset=UTF-8",
                                                      @"image/*",
                                                      @"text/xml",
                                                      nil];
    /// 开启网络监测
    [[AFNetworkActivityIndicatorManager sharedManager] setEnabled:true];
    @weakify(self);
    [self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        @strongify(self);
        [JDStatusBarNotification dismiss];
        if (status == AFNetworkReachabilityStatusUnknown) {
            UIView * viewStaus = [JDStatusBarNotification showWithStatus:@"网络状态未知" styleName:JDStatusBarStyleWarning];
            if (kMMWPTFC().IS_IPHONE_X) {
                viewStaus.mh_y += kMMWPTFC().kNavSafeArea+120;//kSafeAreaTop;
            }
            viewStaus.backgroundColor = [viewStaus.backgroundColor colorWithAlphaComponent:0.3];
            [JDStatusBarNotification showActivityIndicator:YES indicatorStyle:UIActivityIndicatorViewStyleWhite];
            DTLog(@"--- 未知网络 ---");
        }else if (status == AFNetworkReachabilityStatusNotReachable) {
            UIView * viewStaus = [JDStatusBarNotification showWithStatus:@"网络不给力，请检查网络" styleName:JDStatusBarStyleWarning];
            if (kMMWPTFC().IS_IPHONE_X) {
                viewStaus.mh_y += kMMWPTFC().kNavSafeArea+120;//kSafeAreaTop;
            }
            viewStaus.backgroundColor = [viewStaus.backgroundColor colorWithAlphaComponent:0.3];
            [JDStatusBarNotification showActivityIndicator:YES indicatorStyle:UIActivityIndicatorViewStyleWhite];
            DTLog(@"--- 无网络 ---");
        }else{
            DTLog(@"--- 有网络 ---");
            [JDStatusBarNotification dismiss];
        }
    }];
    [self.reachabilityManager startMonitoring];
}

//MARK: - ==== Token
- (void)setToken:(NSString *)token {
    _token = token;
    if (token != nil) {
        [MDHSwiftHeader save_cacheWithData:token key:[[self className] stringByAppendingString:@"token"]];
    }
}
- (NSString *)token {
    if (!_token) {
        NSString * token = [MDHSwiftHeader get_cacheWithKey:[[self className] stringByAppendingString:@"token"]];
        _token = token.length > 0 ? token:@"";
    }
    if (_token.length <= 0) {
        _token = @"8d8e4cc65719ccf51e70520ef7d951ce";
    }
    return _token;
}

//MARK: User Module
- (void)saveUser:(MDHUserModel *)user {
    /// 记录用户数据
    self.currentUser = user;
    /*if (user.mobile.length > 0) {
        user.phone = user.mobile;
    }
    user.mobile = user.phone;
    user.perfect_number = user.account;
    user.head_pic = user.avatar;
    user.level_img = user.level_icon;
    if (user.is_auth) {
        user.is_real = user.is_auth;
    }
    user.is_auth = user.is_real;*/
    if (user.token.length > 0) {
        self.token = user.token;
    }
    ///用户信息的json
    NSString * u_json = [user mj_JSONString];
    [SAMKeychain saveUserInfo:u_json];
    ///保存其他的信息
    NSString * user_id = [self currentUserId];
    if (user_id.length > 0 && ![user_id isEqualToString:@"0"]) {
        [SAMKeychain setRawLogin:user_id];
    }
    /// 保存
    BOOL status = [NSKeyedArchiver archiveRootObject:u_json toFile:MHFilePathFromWeChatDoc(MHUserDataFileName)];//
    DTLog(@"Save login user data， the status is %@",status?@"Success...":@"Failure...");
}

- (void)deleteUser:(MDHUserModel *)user {
    /// 删除
    self.currentUser.user_id = 0;//[MDHUserModel new];
    [self saveUser:self.currentUser];
    self.token = @"";
    BOOL status = [MHFileManager removeFile:MHFilePathFromWeChatDoc(MHUserDataFileName)];
    DTLog(@"Delete login user data ， the status is %@",status?@"Success...":@"Failure...");
}

- (MDHUserModel *)currentUser {
    if (!_currentUser) {
        NSString * json = [NSKeyedUnarchiver unarchiveObjectWithFile:MHFilePathFromWeChatDoc(MHUserDataFileName) exception:nil];
        if (json.length > 0) {
            _currentUser = [MDHUserModel mj_objectWithKeyValues:json];
        }else{
            json = [SAMKeychain userInfo];
            if (json.length > 0) {
                _currentUser = [MDHUserModel mj_objectWithKeyValues:json];
                _currentUser.user_id = 0;
            }else{
                _currentUser = [MDHUserModel new];
            }
        }
    }
    return _currentUser;
}

///是否登录了
- (BOOL)isLogin {
    return [self currentUser].user_id != 0;
}

/// 获取当前用户的id
- (NSString *)currentUserId {
    return [@([self currentUser].user_id) stringValue];
}

///保存 当前用户的钻石
- (void)saveDiamondMoney:(NSString *)money {
    if (money == nil) {
        return;
    }
    //self.currentUser.diamond_money = money;
    [self saveUser:self.currentUser];
}
///保存 当前用户的金币
- (void)saveGoldMoney:(NSString *)money {
    if (money == nil) {
        return;
    }
    //self.currentUser.gold = money;
    [self saveUser:self.currentUser];
}

- (void)loginUser:(MDHUserModel *)user {
    /// 保存用户
    [self saveUser:user];
    
    /// 发送登录成功的通知
    [self postUserDataConfigureCompleteNotification];
    
    /// 设置别名
    //[SBJPushService setAlias];
}

/// 退出登录
- (void)logoutUser {
    MDHUserModel *currentUser = [self currentUser];
    /// 删除别名
    //[SBJPushService deleteAlias];
    //
    ///// 删除token
    //[self deleteToken];
    
    /// 删除用户数据
    [self deleteUser:currentUser];
}

///用户信息配置完成
- (void)postUserDataConfigureCompleteNotification {
    MDHUserModel * user = [self currentUser];
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotification_LoginSuccess object:nil userInfo:@{MHUserDataConfigureCompleteUserInfoKey: user}];
}


//MARK: - ===== 私有方法
//MARK: - === 打印请求日志
- (void)HTTPRequestLog:(NSURLSessionTask *)task body:params error:(NSError *)error response:(id)responseObject {
    //self.isGetCache = false;
    //self.isSetCache = false;
 
    DTLog(@">>>>>>>>>>>>>>>>>>>>>👇 REQUEST FINISH 👇>>>>>>>>>>>>>>>>>>>>>>>>>>");
    DTLog(@"Request%@=======>:%@", error?@"失败":@"成功", task.currentRequest.URL.absoluteString);
    DTLog(@"requestBody======>请求参数:%@", params);
    DTLog(@"requstHeader=====>:%@", task.currentRequest.allHTTPHeaderFields);
    DTLog(@"response=========>:%@", task.response);
    if (responseObject) {
        DTLog(@"responseObject=========>返回值:%@", [responseObject mj_JSONString]);
    }else{
        DTLog(@"responseObject=========>返回值:%@", responseObject);
    }
    DTLog(@"error============>:%@", error);
    DTLog(@"<<<<<<<<<<<<<<<<<<<<<👆 REQUEST FINISH 👆<<<<<<<<<<<<<<<<<<<<<<<<<<");
    
    ///存储token
    NSHTTPURLResponse *httpUrlResponse = (NSHTTPURLResponse *)task.response;
    if (httpUrlResponse) {
        NSDictionary * allHeaderFields = [httpUrlResponse allHeaderFields];
        NSString * dateStr = [allHeaderFields valueForKey:@"Date"];
        ///网络时间
        NSDate * netDate = [NSDate dateFromRFC822String:dateStr];
        dateStr = [netDate stringWithFormat:@"yyyy-MM-dd HH:mm:ss"];
        /*
        NSTimeZone * timeZone = [NSTimeZone timeZoneForSecondsFromGMT:8*60*60];
        NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
        [formatter setTimeZone:timeZone];
        [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        NSString * beijingtimeStr = [formatter stringFromDate:date];
        DTLog(@"- ====== 服务器时间 ====== ->:%@, Str:%@, Str:%@",date, dateStr, beijingtimeStr);*/

        ///计算 网络时间与本地时间的差值
        ///本地时间
        NSDate * localDate = [NSDate date];
        ///本地时间字符串
        //NSString * localDateStr = [localDate stringWithFormat:@"yyyy-MM-dd HH:mm:ss"];
        
        ///网络与本地时间对比 >0比本地时间快 <0比本地时间小
        double time_nl_cz = [netDate timeIntervalSinceDate:localDate];
        //double time_ln_cz = [localDate timeIntervalSinceDate:netDate];
        self.diffTimeNetLoc = time_nl_cz;
        //DTLog(@"- ====== 服务器时间(%@<-->%@) 与 本地时间(%@<-->%@)的差值 %f<-->%f",netDate, dateStr, localDate, localDateStr, time_nl_cz, time_ln_cz);
        ///存储token
        NSString *token = [[allHeaderFields valueForKey:dh_RequestTokenKey] mh_stringValueExtension];
        if (token.length > 0) {
        #if defined(DEBUG)||defined(_DEBUG)
            if (![token isEqualToString:self.token]) DTLog(@"Token已过期,现已更新~");
        #endif
            self.token = token;
        }
    }
    
    ///解析401错误 需要重新登录
    if (error && responseObject) {
        NSInteger statusCode = [responseObject[dh_ResponseCodeKey] integerValue];
        if (statusCode == MHHTTPResponseCodeTokenExpired) {///用户token过期
            [[NSNotificationCenter defaultCenter] postNotificationName:@"MHHTTPResponseCodeTokenExpired" object:error];
        }
    }
}

///错误解析
- (NSError *)parsingErrorWithFailureReason:(NSString *)localizedFailureReason {
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[NSLocalizedDescriptionKey] = NSLocalizedString(@"Could not parse the service response.", @"");
    if (localizedFailureReason != nil) userInfo[NSLocalizedFailureReasonErrorKey] = localizedFailureReason;
    return [NSError errorWithDomain:MHHTTPServiceErrorDomain code:MHHTTPServiceErrorJSONParsingFailed userInfo:userInfo];
}

/// 解析数据
- (RACSignal *)parsedResponseOfClass:(Class)resultClass fromJSON:(NSDictionary *)responseObject {
    /// 必须是MHObject的子类 且 最外层responseObject必须是字典
    NSParameterAssert((resultClass == nil || [resultClass isSubclassOfClass:MDHPTModel.class]));
    
    /// 这里主要解析的是 data:对应的数据
    responseObject = responseObject[dh_ResponseDataKey];
    
    /// 解析
    return [RACSignal createSignal:^ id (id<RACSubscriber> subscriber) {
        /// 解析字典
        void (^parseJSONDictionary)(NSDictionary *) = ^(NSDictionary *JSONDictionary) {
            if (resultClass == nil) {
                [subscriber sendNext:JSONDictionary];
                return;
            }
            /// 这里继续取出数据 data{"list":[]}
            NSArray * JSONArray = JSONDictionary[dh_ResponseDataListKey];
            if ([JSONArray isKindOfClass:[NSArray class]]) {
                /// 字典数组 转对应的模型
                NSArray *parsedObjects = [[[resultClass class] mj_objectArrayWithKeyValuesArray:JSONArray] copy];
                /// 这里还需要解析是否是MHObject的子类
                for (id parsedObject in parsedObjects) {
                    /// 确保解析出来的类 也是 MHObject
                    NSAssert([parsedObject isKindOfClass:MDHPTModel.class], @"Parsed model object is not an MHObject: %@", parsedObject);
                }
                [subscriber sendNext:parsedObjects];
            }else{
                /// 字典转模型
                MDHPTModel *parsedObject = [[resultClass class] mj_objectWithKeyValues:JSONDictionary];
                if (parsedObject == nil) {
                    // Don't treat "no class found" errors as real parsing failures.
                    // In theory, this makes parsing code forward-compatible with
                    // API additions.
                    // 模型解析失败
                    NSError *error = [NSError errorWithDomain:@"" code:2222 userInfo:@{}];
                    [subscriber sendError:error];
                    return;
                }
                
                /// 确保解析出来的类 也是 BaseModel
                NSAssert([parsedObject isKindOfClass:MDHPTModel.class], @"Parsed model object is not an BaseModel: %@", parsedObject);
                /// 发送数据
                [subscriber sendNext:parsedObject];
            }
        };
        
        if ([responseObject isKindOfClass:NSArray.class]) {
            
            if (resultClass == nil) {
                [subscriber sendNext:responseObject];
            }else{
                /// 数组 保证数组里面装的是同一种 NSDcitionary
                for (NSDictionary *JSONDictionary in responseObject) {
                    if (![JSONDictionary isKindOfClass:NSDictionary.class]) {
                        //NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Invalid JSON array element: %@", @""), JSONDictionary];
                        //[subscriber sendError:[self parsingErrorWithFailureReason:failureReason]];
                        [subscriber sendNext:responseObject];
                        [subscriber sendCompleted];
                        return nil;
                    }
                }
                
                /// 字典数组 转对应的模型
                NSArray *parsedObjects = [[resultClass class] mj_objectArrayWithKeyValuesArray:responseObject];
                /// 这里还需要解析是否是MHObject的子类
                for (id parsedObject in parsedObjects) {
                    /// 确保解析出来的类 也是 BaseModel
                    NSAssert([parsedObject isKindOfClass:MDHPTModel.class], @"Parsed model object is not an BaseModel: %@", parsedObject);
                }
                [subscriber sendNext:parsedObjects];
            }
            [subscriber sendCompleted];
        } else if ([responseObject isKindOfClass:NSDictionary.class]) {
            /// 解析字典
            parseJSONDictionary(responseObject);
            [subscriber sendCompleted];
        } else if (responseObject == nil || [responseObject isKindOfClass:[NSNull class]]) {
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
        } else {
            //NSString *failureReason = [NSString stringWithFormat:NSLocalizedString(@"Response wasn't an array or dictionary (%@): %@", @""), [responseObject class], responseObject];
            //[subscriber sendError:[self parsingErrorWithFailureReason:failureReason]];
            [subscriber sendNext:responseObject];
            [subscriber sendCompleted];
            return nil;
        }
        return nil;
    }];
}

///返回缓存的数据
- (void)getCacheData:(id<RACSubscriber>)subscriber path:(NSString *)path parameters:(id)parameters {
    /*///获取缓存
    if (self.isGetCache) {///当前的数据是否需要缓存
        id d = [MHFileManager httpCacheForURL:path parameters:parameters];
        if (d) {/// 打包成元祖 回调数据
            [subscriber sendNext:RACTuplePack(d , d)];
        }
    }*/
}

//MARK: - ==== 普通的 请求数据
- (RACSignal *)enqueueRequestWithPath:(NSString *)path parameters:(id)parameters method:(NSString *)method {
    @weakify(self);
    ///创建信号
    RACSignal * signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self);
        if ([self getProxyStatus]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            NSInteger statusCode = MHHTTPResponseCodeParametersVerifyFailure;
            NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
            userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
            NSString *msgTips = @"";
#if defined(DEBUG)||defined(_DEBUG)
            msgTips = @"请关闭代理，稍后重试~";/// 调试模式
#else
            msgTips = @"请关闭代理，稍后重试~";/// 发布模式
#endif
            userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
            NSError *requestError = [NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo];
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                [subscriber sendError:requestError];
                [subscriber sendCompleted];
            });
#pragma clang diagnostic pop
            return [RACDisposable disposableWithBlock:^{
            }];
        }
        /// 获取request
        NSError *serializationError = nil;
        NSMutableURLRequest * request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:path relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        if (serializationError) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                [subscriber sendError:serializationError];
                [subscriber sendCompleted];
            });
#pragma clang diagnostic pop
            return [RACDisposable disposableWithBlock:^{
            }];
        }
        //NSString * urlPath = request.URL.absoluteString;
        ///获取缓存数据
        //[self getCacheData:subscriber path:urlPath parameters:parameters];
        
        /// 获取请求任务
        __block NSURLSessionDataTask *task = nil;
        task = [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            if (error) {
                NSError *parseError = [self _errorFromRequestWithTask:task httpResponse:(NSHTTPURLResponse *)response responseObject:responseObject error:error];
                [self HTTPRequestLog:task body:parameters error:parseError response:responseObject];
                [subscriber sendError:parseError];
                [subscriber sendCompleted];
            }else{
                /// 断言
                NSAssert([responseObject isKindOfClass:NSDictionary.class], @"responseObject is not an NSDictionary: %@", responseObject);
                /// 在这里判断数据是否正确
                NSDictionary * data_dict = [self decrypt_dict:responseObject];
                if (data_dict) {
                    responseObject = data_dict;
                }
                /// 判断
                NSInteger statusCode = [responseObject[dh_ResponseCodeKey] integerValue];
                if (statusCode == MHHTTPResponseCodeSuccess) {
                    [self HTTPRequestLog:task body:parameters error:nil response:responseObject];
                    /// 打包成元祖 回调数据
                    [subscriber sendNext:RACTuplePack(response , responseObject)];
                    [subscriber sendCompleted];
                }else{
                    if (statusCode == MHHTTPResponseCodeNotLogin) {
                        /*
                        /// 需要登录
                        [self login:^{
                            /// 这里需要重新配置序列化
                            self.requestSerializer = [self _requestSerializerWithRequest:[MDHHTTPRequest requestWithParameters:[MDHURLParameters urlParametersWithMethod:method path:path parameters:parameters]]];
                            /// 重新发起请求
                            [[self enqueueRequestWithPath:path parameters:parameters method:method] subscribe:subscriber];
                        } cancel:^{
                            NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                            userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
                            NSString *msgTips = responseObject[dh_ResponseMsgKey];
#if defined(DEBUG)||defined(_DEBUG)
                            msgTips = msgTips.length > 0 ? [NSString stringWithFormat:@"%@(%zd)",msgTips,statusCode]:[NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",statusCode];/// 调试模式
#else
                            msgTips = msgTips.length > 0 ?msgTips:@"网络开小差了，请稍后重试~";/// 发布模式
#endif
                            userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
                            if (task.currentRequest.URL != nil) userInfo[MHHTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                            if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                            NSError *requestError = [NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                            [self HTTPRequestLog:task body:parameters error:nil response:responseObject];
                            [subscriber sendError:requestError];
                        }];
                        */
                    }else{
                        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                        userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
                        NSString *msgTips = responseObject[dh_ResponseMsgKey];
#if defined(DEBUG)||defined(_DEBUG)
                        msgTips = msgTips.length > 0 ? [NSString stringWithFormat:@"%@(%zd)",msgTips,statusCode]:[NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",statusCode];/// 调试模式
#else
                        msgTips = msgTips.length > 0 ?msgTips:@"网络开小差了，请稍后重试~";/// 发布模式
#endif
                        userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
                        if (responseObject) {
                            userInfo[MDHHTTPServiceResponseErrorDataKey] = responseObject;
                        }
                        if (task.currentRequest.URL != nil) userInfo[MHHTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                        if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                        NSError *requestError = [NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                        [self HTTPRequestLog:task body:parameters error:requestError response:responseObject];
                        [subscriber sendError:requestError];
                        [subscriber sendCompleted];
                    }
                }
            }
        }];
        /// 开启请求任务
        [task resume];
        return [RACDisposable disposableWithBlock:^{
            [task cancel];
        }];
    }];
    /// replayLazily:replayLazily会在第一次订阅的时候才订阅sourceSignal
    /// 会提供所有的值给订阅者 replayLazily还是冷信号 避免了冷信号的副作用
    return [[signal replayLazily] setNameWithFormat:@"-enqueueRequestWithPath: %@ parameters: %@ method: %@",path, parameters , method];
}

//MARK: - ==== 上传文件的方法
- (RACSignal *)enqueueUploadRequestWithPath:(NSString *)path parameters:(id)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block {
    @weakify(self);
    /// 创建信号
    RACSignal *signal = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        @strongify(self);
        if ([self getProxyStatus]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            NSInteger statusCode = MHHTTPResponseCodeParametersVerifyFailure;
            NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
            userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
            NSString *msgTips = @"";
#if defined(DEBUG)||defined(_DEBUG)
            msgTips = @"请关闭代理，稍后重试~";/// 调试模式
#else
            msgTips = @"请关闭代理，稍后重试~";/// 发布模式
#endif
            userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
            NSError *requestError = [NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo];
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                [subscriber sendError:requestError];
            });
#pragma clang diagnostic pop
            return [RACDisposable disposableWithBlock:^{
            }];
        }
        /// 获取request
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:path relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
        if (serializationError) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                [subscriber sendError:serializationError];
            });
#pragma clang diagnostic pop
            return [RACDisposable disposableWithBlock:^{
            }];
        }
        
        __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            if (error) {
                NSError *parseError = [self _errorFromRequestWithTask:task httpResponse:(NSHTTPURLResponse *)response responseObject:responseObject error:error];
                [self HTTPRequestLog:task body:parameters error:parseError response:responseObject];
                [subscriber sendError:parseError];
            } else {
                
                /// 断言
                NSAssert([responseObject isKindOfClass:NSDictionary.class], @"responseObject is not an NSDictionary: %@", responseObject);
                /// 在这里判断数据是否正确
                NSDictionary * data_dict = [self decrypt_dict:responseObject];
                if (data_dict) {
                    responseObject = data_dict;
                }
                /// 判断
                NSInteger statusCode = [responseObject[dh_ResponseCodeKey] integerValue];
                
                if (statusCode == MHHTTPResponseCodeSuccess) {
                    [self HTTPRequestLog:task body:parameters error:nil response:responseObject];
                    /// 打包成元祖 回调数据
                    [subscriber sendNext:RACTuplePack(response , responseObject)];
                    [subscriber sendCompleted];
                }else{
                    
                    if (statusCode == MHHTTPResponseCodeNotLogin) {
                        
                        /// 需要登录
                        //[self login:^{
                        ///// 这里需要重新配置序列化
                        //self.requestSerializer = [self _requestSerializerWithRequest:[MHHTTPRequest requestWithParameters:[MHURLParameters urlParametersWithMethod:@"POST" path:path parameters:parameters]]];
                        ///// 重新发起请求
                        //[self enqueueUploadRequestWithPath:path parameters:parameters constructingBodyWithBlock:block];
                        // } cancel:^{
                        //     NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                        //     userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
                        //     NSString *msgTips = responseObject[dh_ResponseMsgKey];
                        //#if defined(DEBUG)||defined(_DEBUG)
                        //    msgTips = MHStringIsNotEmpty(msgTips)?[NSString stringWithFormat:@"%@(%zd)",msgTips,statusCode]:[NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",statusCode];                 /// 调试模式
                        //#else
                        //   msgTips = MHStringIsNotEmpty(msgTips)?msgTips:@"网络开小差了，请稍后重试~";  /// 发布模式
                        //#endif
                        //userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
                        //if (task.currentRequest.URL != nil) userInfo[MHHTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                        //if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                        //[subscriber sendError:[NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo]];
                        //}];
                    }else{
                        
                        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
                        userInfo[dh_ErrorResponseCodeKey] = @(statusCode);
                        NSString *msgTips = responseObject[dh_ResponseMsgKey];
#if defined(DEBUG)||defined(_DEBUG)
                        msgTips = msgTips.length > 0?[NSString stringWithFormat:@"%@(%zd)",msgTips,statusCode]:[NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",statusCode];                 /// 调试模式
#else
                        msgTips = msgTips.length > 0?msgTips:@"网络开小差了，请稍后重试~";  /// 发布模式
#endif
                        userInfo[MHHTTPServiceErrorMessagesKey] = msgTips;
                        if (responseObject) {
                            userInfo[MDHHTTPServiceResponseErrorDataKey] = responseObject;
                        }
                        if (task.currentRequest.URL != nil) userInfo[MHHTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
                        if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
                        NSError *requestError = [NSError errorWithDomain:MHHTTPServiceErrorDomain code:statusCode userInfo:userInfo];
                        [self HTTPRequestLog:task body:parameters error:requestError response:responseObject];
                        [subscriber sendError:requestError];
                        [subscriber sendCompleted];
                    }
                }
            }
        }];
        
        [task resume];
        return [RACDisposable disposableWithBlock:^{
            [task cancel];
        }];
        
    }];
    /// replayLazily:replayLazily会在第一次订阅的时候才订阅sourceSignal
    /// 会提供所有的值给订阅者 replayLazily还是冷信号 避免了冷信号的副作用
    return [[signal
             replayLazily]
            setNameWithFormat:@"-enqueueUploadRequestWithPath: %@ parameters: %@", path, parameters];
}

//MARK: - Error Handling
/// 请求错误解析
- (NSError *)_errorFromRequestWithTask:(NSURLSessionTask *)task httpResponse:(NSHTTPURLResponse *)httpResponse responseObject:(NSDictionary *)responseObject error:(NSError *)error {
    /// 不一定有值，则HttpCode = 0;
    NSInteger HTTPCode = httpResponse.statusCode;
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    /// default errorCode is MHHTTPServiceErrorConnectionFailed，意味着连接不上服务器
    NSInteger errorCode = MHHTTPServiceErrorConnectionFailed;
    NSString *errorDesc = @"网络开小差了，请稍后重试~";
    /// 其实这里需要处理后台数据错误，一般包在 responseObject
    /// HttpCode错误码解析 https://www.guhei.net/post/jb1153
    /// 1xx : 请求消息 [100  102]
    /// 2xx : 请求成功 [200  206]
    /// 3xx : 请求重定向[300  307]
    /// 4xx : 请求错误  [400  417] 、[422 426] 、449、451
    /// 5xx 、600: 服务器错误 [500 510] 、600
    NSInteger httpFirstCode = HTTPCode/100;
    if (httpFirstCode>0) {
        if (httpFirstCode==4) {
            /// 请求出错了，请稍后重试
            if (HTTPCode == 408) {
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = @"请求超时，请稍后再试(408)~"; /// 调试模式
#else
                errorDesc = @"请求超时，请稍后再试~";      /// 发布模式
#endif
            }else{
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = [NSString stringWithFormat:@"请求出错了，请稍后重试(%zd)~",HTTPCode];                   /// 调试模式
#else
                errorDesc = @"请求出错了，请稍后重试~";      /// 发布模式
#endif
            }
        }else if (httpFirstCode == 5 || httpFirstCode == 6){
            /// 网络开小差了，请稍后重试
#if defined(DEBUG)||defined(_DEBUG)
            errorDesc = [NSString stringWithFormat:@"服务器出错了，请稍后重试(%zd)~",HTTPCode];                      /// 调试模式
#else
            errorDesc = @"网络开小差了，请稍后重试~";       /// 发布模式
#endif
            
        }else if (!self.reachabilityManager.isReachable){
            /// 网络不给力，请检查网络
            errorDesc = @"网络开小差了，请稍后重试~";
        }
    }else{
        if (!self.reachabilityManager.isReachable){
            /// 网络不给力，请检查网络
            errorDesc = @"网络开小差了，请稍后重试~";
        }
    }
    switch (HTTPCode) {
        case 400:{
            errorCode = MHHTTPServiceErrorBadRequest;           /// 请求失败
            break;
        }
        case 403:{
            errorCode = MHHTTPServiceErrorRequestForbidden;     /// 服务器拒绝请求
            break;
        }
        case 422:{
            errorCode = MHHTTPServiceErrorServiceRequestFailed; /// 请求出错
            break;
        }
        default:
            /// 从error中解析
            if ([error.domain isEqual:NSURLErrorDomain]) {
#if defined(DEBUG)||defined(_DEBUG)
                errorDesc = [NSString stringWithFormat:@"请求出错了，请稍后重试(%zd)~",error.code];                   /// 调试模式
#else
                errorDesc = @"请求出错了，请稍后重试~";        /// 发布模式
#endif
                switch (error.code) {
                    case NSURLErrorSecureConnectionFailed:
                    case NSURLErrorServerCertificateHasBadDate:
                    case NSURLErrorServerCertificateHasUnknownRoot:
                    case NSURLErrorServerCertificateUntrusted:
                    case NSURLErrorServerCertificateNotYetValid:
                    case NSURLErrorClientCertificateRejected:
                    case NSURLErrorClientCertificateRequired:
                        errorCode = MHHTTPServiceErrorSecureConnectionFailed; /// 建立安全连接出错了
                        break;
                    case NSURLErrorTimedOut:{
#if defined(DEBUG)||defined(_DEBUG)
                        errorDesc = @"请求超时，请稍后再试(-1001)~"; /// 调试模式
#else
                        errorDesc = @"网络不给力，请稍后再试~";        /// 发布模式
#endif
                        break;
                    }
                    case NSURLErrorNotConnectedToInternet:{
#if defined(DEBUG)||defined(_DEBUG)
                        errorDesc = @"网络开小差了，请稍后重试(-1009)~"; /// 调试模式
#else
                        errorDesc = @"网络开小差了，请稍后重试~";        /// 发布模式
#endif
                        break;
                    }
                }
            }
    }
    userInfo[MHHTTPServiceErrorHTTPStatusCodeKey] = @(HTTPCode);
    userInfo[MHHTTPServiceErrorMessagesKey] = errorDesc;//[errorDesc stringByAppendingFormat:@"%ld,url:,返回的数据:%@, error:%@",HTTPCode,task.currentRequest.URL.absoluteString,[responseObject mj_JSONString], error.userInfo];
    if (task.currentRequest.URL != nil) userInfo[MHHTTPServiceErrorRequestURLKey] = task.currentRequest.URL.absoluteString;
    if (task.error != nil) userInfo[NSUnderlyingErrorKey] = task.error;
    //AlertViewShowOne([NSString stringWithFormat:@"%@",userInfo]);
    return [NSError errorWithDomain:MHHTTPServiceErrorDomain code:errorCode userInfo:userInfo];
}

//MARK: - Parameter 签名 MD5 生成一个 sign ，这里请根据实际项目来定
///请求参数 真实的参数 基础的请求参数
-(NSDictionary *)_parametersWithRequest:(MDHHTTPRequest *)request {
    NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:1];
    if ([request.urlParameters.parameters count]) {
        [parameters addEntriesFromDictionary:request.urlParameters.parameters];
    }
    /*///判断是否有user_id
    if (![parameters containsObjectForKey:@"user_id"]) {
        parameters[@"user_id"] = self.currentUserId;
    }*/
    return parameters.copy;
}
///请求头的参数
-(NSDictionary *)_parametersHeaderWithRequest:(MDHHTTPRequest *)request {
    NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:1];
    MDHURLExtendsParameters * extendsParameters = request.urlParameters.extendsParameters;
    self.version = extendsParameters.version;
    /*///判断是否有user_id
    if (![parameters containsObjectForKey:@"user_id"]) {
        parameters[@"user_id"] = self.currentUserId;
    }*/
    /// 模型转字典
    NSDictionary *extendsUrlParams = ((NSDictionary *)[extendsParameters mj_JSONObject]).copy;
    if ([extendsUrlParams count]) {
        [parameters addEntriesFromDictionary:extendsUrlParams];
    }
    return parameters.copy;
}
///请求头 和 基础参数都需要
-(NSDictionary *)_parametersAllWithRequest:(MDHHTTPRequest *)request {
    NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:1];
    ///添加请求有参数
    [parameters addEntriesFromDictionary:[self _parametersHeaderWithRequest:request]];
    ///添加真实的请求参数
    [parameters addEntriesFromDictionary:[self _parametersWithRequest:request]];
    [parameters removeObjectForKey:@"authorization"];
    [parameters removeObjectForKey:@"time_stamp"];
    return parameters.copy;
}

/// 基础的请求参数
- (NSMutableDictionary *)_parametersWithRequest:(MDHHTTPRequest *)request userId:(BOOL)isUid {
    NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
    /// 模型转字典
    NSDictionary *extendsUrlParams = ((NSDictionary *)[request.urlParameters.extendsParameters mj_JSONObject]).copy;
    if ([extendsUrlParams count]) {
        [parameters addEntriesFromDictionary:extendsUrlParams];
    }
    if ([request.urlParameters.parameters count]) {
        [parameters addEntriesFromDictionary:request.urlParameters.parameters];
    }
    /*///判断是否有user_id
    if (isUid && ![parameters containsObjectForKey:@"user_id"]) {
        parameters[@"user_id"] = self.currentUserId;
    }*/
    return parameters;
}

/// 带签名的请求参数
-(NSString *)_signWithParameters:(NSDictionary *) parameters {
    ///数据参数
    NSMutableDictionary * dict = parameters.mutableCopy;

    ///进行加密操作
    ///加密的key值
    NSString * xpp_key = dh_PrivateKey;
    /*
    ///所有的参数 key值按照自然排序从大到小排序 a-z
    NSArray * allKeys = [dict allKeys];
    allKeys = [allKeys sortedArrayUsingComparator:^NSComparisonResult(NSString *  _Nonnull obj1, NSString *  _Nonnull obj2) {///NSOrderedSame相同 NSOrderedAscending从大到小 NSOrderedDescending从小到大
        BOOL result = ([obj1 compare:obj2] == NSOrderedDescending);
        return result;
    }];
    
    ///未拼接的keys
    //DTLog(@"未拼接的keys---->:%@",allKeys);
    
    ///将排序后的参数数组按照key=val&key=val的形式组成字符串
    NSMutableString * sb = @"".mutableCopy;
    for (NSString * key in allKeys) {
        NSString * value = dict[key];
        if ([value isKindOfClass:[NSNumber class]]) {
            value = ((NSNumber *)value).stringValue;
        }
        value = [NSString stringWithFormat:@"%@=%@&",key,value];
        [sb appendString:value];
    }
    ///将字符串与XPP_KEY连接，用md5加密一次(32位小写)，得到sign
    //sb = [[sb lowercaseString] mutableCopy];
    sb = [sb substringToIndex:sb.length-1].mutableCopy;
    */
    NSString * px = [MDHSwiftHeader stringSortWithDict:dict isSort: true];
    NSMutableString * sb = [px mutableCopy];
    [sb appendString:xpp_key];
    ///拼接的keys
    //DTLog(@"拼接完的keys---->:%@",sb);
    ///MD5
    NSString * server_sign = [sb md5String];
    return server_sign;
}

/// 序列化
- (AFHTTPRequestSerializer *)_requestSerializerWithRequest:(MDHHTTPRequest *)request {
    ///获取基础参数 (参数+拓展参数)
    NSMutableDictionary * parameters = @{@"token":self.token}.mutableCopy;//[self _parametersHeaderWithRequest:request].mutableCopy;
    ///获取带签名的参数
    //NSString * sign = [self _signWithParameters:parameters];
    ///赋值
    //parameters[dh_SignKey] = [sign length] ? sign:@"";
    ///请求序列化
    AFHTTPRequestSerializer * requestSerializer = [AFHTTPRequestSerializer serializer];
    ///配置请求头
    for (NSString * key in parameters) {
        if ([key isEqualToString:@"t"]) {
            continue;
        }
        NSString * value = [[parameters[key] mh_stringValueExtension] copy];
        if (value.length <= 0) {
            continue;
        }
        /// value只能是字符串，否则崩溃
        [requestSerializer setValue:value forHTTPHeaderField:key];
    }
    return requestSerializer;
}
///加密参数
- (NSDictionary *)_md5DictWithRequest:(MDHHTTPRequest *)request {
    //NSInteger timestamp = [MDHSwiftHeader server_timestamp];
    ///获取基础参数 (参数+拓展参数)
    NSMutableDictionary * parameters = [self _parametersAllWithRequest:request].mutableCopy;
    [parameters setValue:kMMWPTFC().longitude forKey:@"longitude"];
    [parameters setValue:kMMWPTFC().latitude forKey:@"latitude"];
    ///获取带签名的参数
    NSString * sign = [self _signWithParameters:parameters.copy];
    ///赋值
    parameters[dh_SignKey] = [sign length] ? sign:@"";
    DTLog(@"\n\n\n请求的网址:%@\n请求的参数=====>:\n%@",request.urlParameters.path, parameters);
    DTLog(@"\n\n\n");
    /*///所有的参数加密
    NSMutableString * sb = @"".mutableCopy;
    for (NSString * key in parameters.allKeys) {
        NSString * value = parameters[key];
        if ([value isKindOfClass:[NSNumber class]]) {
            value = ((NSNumber *)value).stringValue;
        }
        value = [NSString stringWithFormat:@"%@《%@》",key,value];
        [sb appendString:value];
    }
    NSString * sign_data = [sb substringToIndex:sb.length-1];*/
    NSString * sign_data = [MDHSwiftHeader stringSortWithDict:parameters isSort: false];
    ///加密的key
    NSString * server_sign = [MDHSwiftHeader stringAES128ECBWithData:sign_data key:self.token iv:@"99999999"];
    /*NSString * user_key = [self.token md5String];//[self.token base64DecodedString];
    ///AES-128-ECB
    CocoaSecurityResult * result = [CocoaSecurity aesEncrypt:sign_data hexKey:[user_key substringWithRange:NSMakeRange(8, 16)] hexIv:@"1234567890123456"];
    NSString * server_sign = result.base64;
    ///之后base64
    server_sign = [server_sign base64EncodedString];
    server_sign = [self tihuanzifuchuan:server_sign fan:false];*/
    ///拼接的keys
    //DTLog(@"加密完的---->:%@",server_sign);
    return @{MDHHTTPServiceResponseErrorDataKey: server_sign};
}

///数据解密
- (NSDictionary *)decrypt_dict:(NSDictionary *)dict {
    NSString * data = dict[MDHHTTPServiceResponseErrorDataKey];
    //解密
    data = [MDHSwiftHeader stringDecryptWithDa:data key:self.token iv:@"99999999" isRe:true];
    return [data mj_JSONObject];
}

@end


//MARK: - ==== 请求类
@implementation MDHHTTPService (Request)

/// 1. 使用须知：后台返回数据的保证为👇固定格式 且`data:{}`必须为`字典`或者`NSNull`;
/// {
///    code：0,
///    msg: "",
///    data:{
///    }
/// }
/// 这个方法返回的 signal 将会 send `MHHTTPResponse`这个实例，`parsedResult`就是对应键data对应的值， 如果你想获得里面的parsedResult实例，请使用以下方法
/// [[self enqueueRequest:request resultClass:SBUser.class] sb_parsedResults];
/// 这样取出来的就是 SBUser对象

/// 2.使用方法如下
/*
 /// 1. 配置参数
 SBKeyedSubscript *subscript = [SBKeyedSubscript subscript];
 subscript[@"page"] = @1;
 
 /// 2. 配置参数模型
 SBURLParameters *paramters = [SBURLParameters urlParametersWithMethod:@"GET" path:SUProduct parameters:subscript.dictionary];
 
 /// 3. 创建请求
 /// 3.1 resultClass 传入对象必须得是 MHObject的子类
 /// 3.2 resultClass 传入nil ，那么回调回来的值就是，服务器返回来的数据
 [[[[MHHTTPRequest requestWithParameters:paramters]
 enqueueResultClass:[SBGoodsData class]]
 sb_parsedResults]
 subscribeNext:^(SBGoodsData * goodsData) {
 /// 成功回调
 
 } error:^(NSError *error) {
 /// 失败回调
 
 } completed:^{
 /// 完成
 
 }];
 
 */

/**
 Enqueues a request to be sent to the server.
 This will automatically fetch a of the given endpoint. Each object
 from each page will be sent independently on the returned signal, so
 subscribers don't have to know or care about this pagination behavior.
 
 @param request config the request
 @param resultClass A subclass of `MHObject` that the response data should be returned as,
 and will be accessible from the `parsedResult`
 @return Returns a signal which will send an instance of `MHHTTPResponse` for each parsed
 JSON object, then complete. If an error occurs at any point,
 the returned signal will send it immediately, then terminate.
 */
- (RACSignal *)enqueueRequest:(MDHHTTPRequest *)request resultClass:(Class)resultClass {
    ///request
    if (!request) {
        return [RACSignal error:[NSError errorWithDomain:MHHTTPServiceErrorDomain code:-1 userInfo:nil]];
    }

    @weakify(self);
    ///覆盖manager请求序列化
    self.requestSerializer = [self _requestSerializerWithRequest:request];
    /// 发起请求
    /// concat:按一定顺序拼接信号，当多个信号发出的时候，有顺序的接收信号。 这里传进去的参数，不是parameters而是之前通过
    /// urlParametersWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;穿进去的参数
    /*NSMutableDictionary * parameters = self.requestSerializer.HTTPRequestHeaders.mutableCopy;//request.urlParameters.parameters
    if ([parameters containsObjectForKey:@"Accept-Language"]) {
        [parameters removeObjectForKey:@"Accept-Language"];
    }
    if ([parameters containsObjectForKey:@"User-Agent"]) {
        [parameters removeObjectForKey:@"User-Agent"];
    }*/
    ///加密参数
    NSDictionary * parameters = [self _md5DictWithRequest:request];//[[self _parametersAllWithRequest:request] mutableCopy];//;
    return [[[self enqueueRequestWithPath:request.urlParameters.path parameters:parameters.copy method:request.urlParameters.method] reduceEach:^RACStream *(NSURLResponse *response, NSDictionary * responseObject) {
        @strongify(self);
        /// 请求成功 这里解析数据
        return [[self parsedResponseOfClass:resultClass fromJSON:responseObject] map:^id(id parsedResult) {
            MDHHTTPResponse *parsedResponse = [MDHHTTPResponse new];
            if (parsedResult) {
                parsedResponse.result = parsedResult;
            }
            NSDictionary * meta = responseObject[@"meta"];
            if (meta) {
                parsedResponse.meta = [MDHBaseNetMetaModel mj_objectWithKeyValues:meta];
            }
            parsedResponse.text = responseObject[dh_ResponseMsgKey];
            parsedResponse.status = [responseObject[dh_ResponseCodeKey] integerValue];
            NSAssert(parsedResponse != nil, @"Could not create MHHTTPResponse with response %@ and parsedResult %@", response, parsedResult);
            return parsedResponse;
        }];
    }] concat];
}




/**
 用来上传多个文件流，也可以上传单个文件
 
 @param request MHHTTPRequest
 @param resultClass 要转化出来的请求结果且必须是 `MHObject`的子类，否则Crash
 @param fileDatas 要上传的 文件数据，数组里面必须是装着` NSData ` 否则Crash
 @param name  这个是服务器的`资源文件名`，这个服务器会给出具体的数值，不能传nil 否则 Crach
 @param mimeType http://www.jianshu.com/p/a3e77751d37c 如果传nil ，则会传递 application/octet-stream
 @return Returns a signal which will send an instance of `MHHTTPResponse` for each parsed
 JSON object, then complete. If an error occurs at any point,
 the returned signal will send it immediately, then terminate.
 */
- (RACSignal *)enqueueUploadRequest:(MDHHTTPRequest *) request
                        resultClass:(Class /*subclass of MHObject*/) resultClass
                          fileDatas:(NSArray <NSData *> *)fileDatas
                               name:(NSString *)name
                           mimeType:(NSString *)mimeType {
    /// request 必须的有值
    if (!request) return [RACSignal error:[NSError errorWithDomain:MHHTTPServiceErrorDomain code:-1 userInfo:nil]];
    /// 断言
    NSAssert(name.length > 0, @"name is empty: %@", name);
    
    @weakify(self);
    
    /// 覆盖manager 请求序列化
    self.requestSerializer = [self _requestSerializerWithRequest:request];
    /// 发起请求
    /// concat:按一定顺序拼接信号，当多个信号发出的时候，有顺序的接收信号。 这里传进去的参数，不是parameters而是之前通过
    /// urlParametersWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;穿进去的参数
    ///加密参数
    NSDictionary * parameters = [self _md5DictWithRequest:request];//[[self _parametersAllWithRequest:request] mutableCopy];//;
    return [[[self enqueueUploadRequestWithPath:request.urlParameters.path parameters:parameters.copy constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        NSInteger count = fileDatas.count;
        for (int i = 0; i< count; i++) {
            /// 取出fileData
            NSData *fileData = fileDatas[i];
            
            /// 断言
            NSAssert([fileData isKindOfClass:NSData.class], @"fileData is not an NSData class: %@", fileData);
            
            // 在网络开发中，上传文件时，是文件不允许被覆盖，文件重名
            // 要解决此问题，
            // 可以在上传时使用当前的系统事件作为文件名
            
            static NSDateFormatter *formatter = nil;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                formatter = [[NSDateFormatter alloc] init];
            });
            // 设置时间格式
            [formatter setDateFormat:@"yyyyMMddHHmmss"];
            NSString *dateString = [formatter stringFromDate:[NSDate date]];
            NSString *fileName = [NSString  stringWithFormat:@"senba_empty_%@_%d.jpg", dateString , i];
            NSString * nameFile = name;
            if (count > 1) {
                nameFile = [nameFile stringByAppendingString:[@(i+1) stringValue]];
            }
            [formData appendPartWithFileData:fileData name:nameFile fileName:fileName mimeType:mimeType.length>0?mimeType:@"application/octet-stream"];
        }
    }] reduceEach:^RACStream *(NSURLResponse *response, NSDictionary * responseObject) {
        @strongify(self);
        /// 请求成功 这里解析数据
        return [[self parsedResponseOfClass:resultClass fromJSON:responseObject]
                map:^(id parsedResult) {
                    MDHHTTPResponse *parsedResponse = [MDHHTTPResponse new];
                    if (parsedResult) {
                        parsedResponse.result = parsedResult;
                    }
                    parsedResponse.text = responseObject[dh_ResponseMsgKey];
                    parsedResponse.status = [responseObject[dh_ResponseCodeKey] integerValue];
                    NSAssert(parsedResponse != nil, @"Could not create MHHTTPResponse with response %@ and parsedResult %@", response, parsedResult);
                    return parsedResponse;
                }];
    }] concat];;
}

/// 上传多个 本地地址文件
- (RACSignal *)enqueueUploadFileRequest:(MDHHTTPRequest *) request
                            resultClass:(Class /*subclass of MHObject*/) resultClass
                              fileUrls:(NSArray <NSString *> *)fileUrls
                                   name:(NSString *)name
                               mimeType:(NSString *)mimeType {
   /// request 必须的有值
   if (!request) return [RACSignal error:[NSError errorWithDomain:MHHTTPServiceErrorDomain code:-1 userInfo:nil]];
   /// 断言
   NSAssert(name.length > 0, @"name is empty: %@", name);
   
   @weakify(self);
   
   /// 覆盖manager 请求序列化
   self.requestSerializer = [self _requestSerializerWithRequest:request];
   /// 发起请求
   /// concat:按一定顺序拼接信号，当多个信号发出的时候，有顺序的接收信号。 这里传进去的参数，不是parameters而是之前通过
   /// urlParametersWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;穿进去的参数
   ///加密参数
   NSDictionary * parameters = [self _md5DictWithRequest:request];//[[self _parametersAllWithRequest:request] mutableCopy];//;
   return [[[self enqueueUploadRequestWithPath:request.urlParameters.path parameters:parameters.copy constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
       NSInteger count = fileUrls.count;
       for (int i = 0; i< count; i++) {
           /// 取出fileData
           NSString *fileUrl = fileUrls[i];
           
           /// 断言
           NSAssert([fileUrl isKindOfClass:NSString.class], @"fileData is not an NSString class: %@", fileUrl);
           
           // 在网络开发中，上传文件时，是文件不允许被覆盖，文件重名
           // 要解决此问题，
           // 可以在上传时使用当前的系统事件作为文件名
           /*
           static NSDateFormatter *formatter = nil;
           static dispatch_once_t onceToken;
           dispatch_once(&onceToken, ^{
               formatter = [[NSDateFormatter alloc] init];
           });
           // 设置时间格式
           [formatter setDateFormat:@"yyyyMMddHHmmss"];
           NSString *dateString = [formatter stringFromDate:[NSDate date]];
           NSString *fileName = [NSString  stringWithFormat:@"senba_empty_%@_%d", dateString , i];*/
           NSString * nameFile = name;
           if (count > 1) {
               nameFile = [nameFile stringByAppendingString:[@(i+1) stringValue]];
           }
           NSError * error = nil;
           [formData appendPartWithFileURL:[NSURL fileURLWithPath:fileUrl] name:nameFile error:&error];
           //[formData appendPartWithFileURL:[NSURL fileURLWithPath:fileUrl] name:nameFile fileName:fileName mimeType:mimeType.length>0?mimeType:@"application/octet-stream" error:&error];
       }
   }] reduceEach:^RACStream *(NSURLResponse *response, NSDictionary * responseObject) {
       @strongify(self);
       /// 请求成功 这里解析数据
       return [[self parsedResponseOfClass:resultClass fromJSON:responseObject]
               map:^(id parsedResult) {
                   MDHHTTPResponse *parsedResponse = [MDHHTTPResponse new];
                   if (parsedResult) {
                       parsedResponse.result = parsedResult;
                   }
                   parsedResponse.text = responseObject[dh_ResponseMsgKey];
                   parsedResponse.status = [responseObject[dh_ResponseCodeKey] integerValue];
                   NSAssert(parsedResponse != nil, @"Could not create MHHTTPResponse with response %@ and parsedResult %@", response, parsedResult);
                   return parsedResponse;
               }];
   }] concat];;
}


+ (id)transPtModel:(MDHPTModel *)model {
    return (id)model;
}

+ (MDHPTModel *)transAnToModel:(id)model {
    return (MDHPTModel *)model;
}

@end
